import { EventDispatcher } from "./EventDispatcher.js";
import { Point } from "./Point.js";
import { RectRubber } from './Rubber.Rect.js';
import { containsPoint } from "./utils/index.js";

class Pencil extends EventDispatcher {
    constructor() {
        super();
        this.path = [];
        this.forceState = 'normal';

        this.stroke = '#c4c4c4';
        this.fill = '#c4c4c4';
        this.lineWidth = 1;

        this.pointRadius = 3;

        this.rubber = new RectRubber();
    }
    get rect() {
        return this.getBoundingBox()
    }
    get x() {
        return this.rect.x;
    }
    get y() {
        return this.rect.y;
    }
    get width() {
        return this.rect.width;
    }
    get height() {
        return this.rect.height;
    }
    set x(x) {
        let dx = x - this.rect.x;
        for (let item of this.path) {
            item.x += dx;
        }
    }
    set y(y) {
        let dy = y - this.rect.y;
        for (let item of this.path) {
            item.y += dy;
        }
    }
    set width(w) {
        let rect = this.rect;

        for (let item of this.path) {
            let ox = item.x - rect.x;
            let p = ox / rect.width;
            let ox2 = w * p;
            item.x = rect.x + ox2;
        }
    }
    set height(h) {
        let rect = this.rect;

        for (let item of this.path) {
            let oy = item.y - rect.y;
            let p = oy / rect.height;
            let oy2 = h * p;
            item.y = rect.y + oy2;
        }
    }

    setup(x, y, w, h) {
        this.x = x;
        this.y = y;
        this.width = w;
        this.height = h;
    }

    addPoint(p) {
        this.path.push(p);
    }

    update(ctx, ox, oy) {

        let rect = this.getBoundingBox();

        this.rubber.update({
            x: rect.x,
            y: rect.y,
            w: rect.width,
            h: rect.height
        });
    }

    draw(ctx, ox, oy) {
        ctx.save();
        ctx.fillStyle = this.fill;
        ctx.strokeStyle = this.stroke;
        ctx.lineWidth = this.lineWidth;
        if (this.path.length != 0) {
            ctx.beginPath();
            ctx.moveTo(this.path[0].x + ox, this.path[0].y + oy);
            for (let i = 1; i < this.path.length; i++) {
                const p = this.path[i];
                ctx.lineTo(p.x + ox, p.y + oy);
                ctx.stroke();
            }
        }

        // this.drawPoint(ctx, ox, oy);
        ctx.restore();

        if (this.forceState == 'selected') {
            this.drawHoverBox(ctx, ox, oy);
            this.rubber.draw(ctx, ox, oy);
        }
        if (this.forceState == 'focus') {
            this.drawHoverBox(ctx, ox, oy);
        }
    }


    drawPoint(ctx, ox, oy) {
        ctx.beginPath();
        for (let i = 0; i < this.path.length; i++) {
            const p = this.path[i];
            ctx.moveTo(p.x, p.y);
            ctx.arc(p.x + ox, p.y + oy, this.pointRadius, 0, Math.PI * 2);
            ctx.fill();
        }
    }

    drawHoverBox(ctx, ox, oy) {
        ctx.save();
        ctx.beginPath();

        let rect = this.getBoundingBox();

        ctx.lineWidth = 2;
        ctx.strokeStyle = '#009DFF';
        ctx.rect(rect.x + ox, rect.y + oy, rect.width, rect.height);
        ctx.stroke();
        ctx.restore();
    }

    containsPoint(ctx, x, y) {
        let rect = this.getBoundingBox(true);
        return containsPoint(rect, x, y);
    }

    containsPoint2(ctx, x, y) {

        const p = new Point(x, y);

        const pointInSegment = (p, a, b) => {
            let distAB = a.distance(b);
            let distAP = a.distance(p);
            let distBP = b.distance(p);
            return Math.abs(distAB - distAP - distBP) <= 0.3;
        }

        for (let i = 0; i < this.path.length - 1; i++) {
            const a = this.path[i];
            const b = this.path[i + 1];
            if (pointInSegment(p, a, b)) {
                return true;
            }
        }

        return false;
    }

    getBoundingBox(containsRubber = false) {

        // let half = this.lineWidth / 2;
        let ax = 0
        let ay = 0
        let aw = 0
        let ah = 0

        if (containsRubber) {
            ax = -3
            ay = -3
            aw = 6
            ah = 6
        }

        if (this.path.length > 0) {

            let minX = this.path[0].x;
            let minY = this.path[0].y;
            let maxX = minX;
            let maxY = minY;

            for (let i = 0; i < this.path.length; i++) {
                const p = this.path[i];
                if (p.x < minX) {
                    minX = p.x
                }
                if (p.x > maxX) {
                    maxX = p.x
                }
                if (p.y < minY) {
                    minY = p.y
                }
                if (p.y > maxY) {
                    maxY = p.y
                }
            }

            return {
                x: minX - this.pointRadius - 3 + ax,
                y: minY - this.pointRadius - 3 + ay,
                width: maxX - minX + this.pointRadius * 2 + aw,
                height: maxY - minY + this.pointRadius * 2 + ah
            }
        }

        return {
            x: 0 + ax,
            y: 0 + ay,
            width: 0 + aw,
            height: 0 + ah
        }
    }

    setPosition(x, y) {
        let rect = this.getBoundingBox();
        let dx = x - rect.x;
        let dy = y - rect.y;

        for (let i = 0; i < this.path.length; i++) {
            this.path[i].x += dx;
            this.path[i].y += dy;
        }
    }

    setForceState(state) {
        if (!['normal', 'focus', 'selected'].includes(state)) {
            throw new TypeError('状态设置错误');
        }
        this.forceState = state;
    }
}


export { Pencil };